[旋转][平移][缩放]仿射变换¶
参考:
opencv 拉伸、扭曲、旋转图像-仿射变换 opencv1 / opencv2 / python cv2(代码)
使用OpenCV
完成图像旋转、平移和缩放操作
仿射变换¶
本质上看仿射变换表示两个图像之间的关系。任何仿射变换均可以由矩阵乘法(线性变换,linear transformation
)加上向量加法(translation
,平移变换)组成。所以通过仿射变换能够完成以下3
个功能
- 旋转(
rotation, linear transformation
) - 平移(
translation, vector addition
) - 缩放(
scale opeation, linear transformation
)
使用2x3
大小数组M
来进行仿射变换。数组由两个矩阵A/B
组成,其中矩阵A
(大小为2x2
)用于矩阵乘法,矩阵B
(大小为2x1
)用于向量加法
将二维图像的坐标点x/y
组成二维数组X
,大小为2xN
,其操作如下:
得到转换后的坐标点再将赋予像素值
函数解析¶
有以下关键函数:
- cv::warpAffine:完成仿射变换
- getRotationMatrix2D():计算旋转矩阵
- getAffineTransform() :从三对对应点中计算仿射变换矩阵
- RotatedRect():计算仿射图像大小
warpAffine¶
CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst,
InputArray M, Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
src
:原图dst
:结果图像。数据深度和类型与原图一致M
:2x3
大小旋转矩阵dsize
:输出图像的大小flags
:插值方法组合(参考InterpolationFlags)。如果设置为WARP_INVERSE_MAP
,表示M
是逆变换borderMode
:边界填充方法,参考BorderTypes。当设置为BORDER_TRANSPARENT
,这意味着目标图像中与源图像中的“异常值”相对应的像素不被函数修改borderValue
:用于固定填充的值;默认情况下为0
函数warpAffine
执行以下操作:
getRotationMatrix2D¶
CV_EXPORTS_W Mat getRotationMatrix2D( Point2f center, double angle, double scale );
center
:源图像中的旋转中心angle
:旋转角度(度)。正值表示逆时针旋转(坐标原点假定为左上角)scale
:各向同性比例因子,就是缩放因子,如果不进行缩放则设置为1.0
函数getRotationMatrix2D
执行如下操作:
其中α
和β
由以下方法得到
getAffineTransform¶
CV_EXPORTS Mat getAffineTransform( const Point2f src[], const Point2f dst[] );
src[]
:原图点坐标dst[]
:结果图对应点坐标
RotatedRect¶
inline
RotatedRect::RotatedRect(const Point2f& _center, const Size2f& _size, float _angle)
: center(_center), size(_size), angle(_angle) {}
_center
:旋转中心_size
:原图大小_angle
:旋转角度
调用函数cv::warpAffine
时设置结果图像大小和原图一致,则仿射图像有可能截断部分内容
示例¶
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char **argv) {
CommandLineParser parser(argc, argv, "{@input | ../lena.jpg | input image}");
Mat src = imread(parser.get<String>("@input"));
if (src.empty()) {
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
// 已知点坐标计算仿射矩阵和结果图像
Point2f srcTri[3];
srcTri[0] = Point2f(0.f, 0.f);
srcTri[1] = Point2f(src.cols - 1.f, 0.f);
srcTri[2] = Point2f(0.f, src.rows - 1.f);
Point2f dstTri[3];
dstTri[0] = Point2f(0.f, src.rows * 0.33f);
dstTri[1] = Point2f(src.cols * 0.85f, src.rows * 0.25f);
dstTri[2] = Point2f(src.cols * 0.15f, src.rows * 0.7f);
Mat warp_mat = getAffineTransform(srcTri, dstTri);
Mat warp_dst = Mat::zeros(src.rows, src.cols, src.type());
warpAffine(src, warp_dst, warp_mat, warp_dst.size());
// 图像以图像中央为旋转中心,顺时针旋转45度
Point center = Point(warp_dst.cols / 2, warp_dst.rows / 2);
double angle = -45.0;
double scale = 1.0;
Mat rot_mat = getRotationMatrix2D(center, angle, scale);
Rect bbox = cv::RotatedRect(center, src.size(), angle).boundingRect();
rot_mat.at<double>(0, 2) += bbox.width / 2.0 - center.x;
rot_mat.at<double>(1, 2) += bbox.height / 2.0 - center.y;
cout << "旋转45度: " << rot_mat << endl;
Mat rotate_dst;
warpAffine(src, rotate_dst, rot_mat, bbox.size());
// 旋转+缩放
scale = src.rows / sqrt(pow(src.rows, 2) * 2);
rot_mat = getRotationMatrix2D(center, angle, scale);
cout << "旋转45度并缩放" << rot_mat << endl;
Mat scale_rotate_dst;
warpAffine(src, scale_rotate_dst, rot_mat, src.size());
// 根据仿射图像得到原图,将图像逆时针旋转45度,然后从中提取原图大小
angle = 45;
scale = 1.0;
center = Point((int) (rotate_dst.cols / 2.0), (int) (rotate_dst.rows / 2.0));
rot_mat = getRotationMatrix2D(center, angle, scale);
Mat affine_src_tmp, affine_src;
warpAffine(rotate_dst, affine_src_tmp, rot_mat, bbox.size());
affine_src = affine_src_tmp(Rect((bbox.width - src.cols) / 2, (bbox.height - src.rows) / 2, src.cols, src.rows));
imshow("Source image", src);
imshow("Warp", warp_dst);
imshow("Rotate", rotate_dst);
imshow("scale_rotate_dst", scale_rotate_dst);
imshow("affine_src", affine_src);
waitKey();
return 0;
}
完成图像变形、旋转、旋转+缩放以及逆旋转的功能